package com.terrier.ui.controls
{
    import com.terrier.ui.core.WxComponent;
    import com.terrier.ui.events.WxButtonEvent;

    import flash.events.MouseEvent;

    /**
     * 当用户在按钮上按下鼠标主键时调度
     * @eventType com.terrier.ui.events.WxButtonEvent.BUTTON_PRESS
     */
    [Event(name = "buttonPress", type = "com.terrier.ui.events.WxButtonEvent")]

    /**
     * 当用户在按钮上按下鼠标主键,随后释放开时调度
     * @eventType com.terrier.ui.events.WxButtonEvent.BUTTON_RELEASE
     */
    [Event(name = "buttonRelease", type = "com.terrier.ui.events.WxButtonEvent")]

    /**
     * 当用户在按钮上单击鼠标主键时调度
     * @eventType com.terrier.ui.events.WxButtonEvent.BUTTON_CLICK
     */
    [Event(name = "buttonClick", type = "com.terrier.ui.events.WxButtonEvent")]

    /**
     * 当用户在按钮上双击鼠标主键时调度
     * @eventType com.terrier.ui.events.WxButtonEvent.BUTTON_DOUBLE_CLICK
     */
    [Event(name = "buttonDoubleClick", type = "com.terrier.ui.events.WxButtonEvent")]

    /**
     * 当按钮的状态改变时调度
     * @eventType com.terrier.ui.events.WxButtonEvent.BUTTON_PHASE_CHANGE
     */
    [Event(name = "buttonPhaseChange", type = "com.terrier.ui.events.WxButtonEvent")]

    /**
     * 按钮基类. 提供了按钮的基本行为特性. 这是一个抽象类, 如果试图创建这个类的实例, 将抛出异常.
     * @author Jarvis.weng
     */
    public class WxButtonBase extends WxComponent
    {
        /**
         * 默认的按钮行为模式
         * @default WxButtonBehavior.PC
         */
        public static var defaultBehavior:String = WxButtonBehavior.PC;

        /**
         * @private
         */
        protected var _phase:String = WxButtonPhase.UP;

        /**
         * @private
         */
        protected var _enabled:Boolean = true;

        private var _behavior:ButtonBehaviorBase;

        /**
         * 构造函数
         */
        public function WxButtonBase()
        {
            abstractDetection();
            behavior = defaultBehavior;
        }

        /**
         * @private
         */
        override public function dispose():void
        {
            super.dispose();
            if (_behavior != null)
            {
                _behavior.dispose();
                _behavior = null;
            }
        }

        /**
         * @private
         */
        public function get behavior():String
        {
            return _behavior.type;
        }

        /**
         * 获取或设置按钮的行为模式. 合法的值包括<code>WxButtonBehavior.PC</code>和<code>WxButtonBehavior.PHONE</code>.
         * @param value
         * @see WxButtonBehavior
         * @throws ArgumentError 如果行为模式的值不是<code>WxButtonBehavior.PC</code>或<code>WxButtonBehavior.PHONE</code>, 将引发异常.
         */
        public function set behavior(value:String):void
        {
            if (_behavior != null)
            {
                if (_behavior.type == value)
                {
                    return;
                }
                _behavior.dispose();
                _behavior = null;
            }

            switch (value)
            {
                case WxButtonBehavior.PHONE:
                    _behavior = new ButtonBehaviorPhone(this, onPhaseChange);
                    break;

                case WxButtonBehavior.PC:
                    _behavior = new ButtonBehaviorPc(this, onPhaseChange);
                    break;

                default:
                    throw new ArgumentError("错误的按钮行为类型");
            }
        }

        /**
         * 获取或设置一个布尔值, 指示按钮是否响应用户的鼠标操作.
         * @default true
         */
        public function get enabled():Boolean
        {
            return _enabled;
        }

        /**
         * @private
         */
        public function set enabled(value:Boolean):void
        {
            if (_enabled == value)
            {
                return;
            }

            _enabled = value;

            onPhaseChange(_enabled ? WxButtonPhase.UP : WxButtonPhase.DISABLED);

            _enabled ? _behavior.initEventListener() : _behavior.disposeEventListener();
        }

        /**
         * @private
         */
        protected function abstractDetection():void
        {
            throw new ArgumentError("不能实例化抽象类");
        }

        private function onPhaseChange(phase:String):void
        {
            if (_phase == phase)
            {
                return;
            }
            var oldPhase:String = _phase;
            _phase = phase;
            invalidateView();
            invalidateLayout();

            dispatchEvent(new WxButtonEvent(WxButtonEvent.BUTTON_PHASE_CHANGE, oldPhase, _phase));
        }
    }
}
import com.terrier.ui.controls.WxButtonBase;
import com.terrier.ui.controls.WxButtonBehavior;
import com.terrier.ui.controls.WxButtonPhase;
import com.terrier.ui.events.WxButtonEvent;

import flash.display.Stage;
import flash.events.EventDispatcher;
import flash.events.MouseEvent;
import flash.utils.getTimer;

class ButtonBehaviorBase
{
    protected var button:WxButtonBase;

    protected var onPhaseChange:Function;

    public function ButtonBehaviorBase(button:WxButtonBase, onPhaseChange:Function)
    {
        this.button = button;
        this.onPhaseChange = onPhaseChange;
        initEventListener();
    }

    public function initEventListener():void
    {
        // nothing to do
    }

    public function disposeEventListener():void
    {
        // nothing to do
    }

    public function dispose():void
    {
        disposeEventListener();
        button = null;
        onPhaseChange = null;
    }

    public function get type():String
    {
        return null;
    }
}

class ButtonBehaviorPhone extends ButtonBehaviorBase
{
    public function ButtonBehaviorPhone(button:WxButtonBase, onPhaseChange:Function)
    {
        super(button, onPhaseChange);
    }

    override public function get type():String
    {
        return WxButtonBehavior.PHONE;
    }

    override public function initEventListener():void
    {
        button.addEventListener(MouseEvent.ROLL_OVER, mouseOverHandler);
    }

    override public function disposeEventListener():void
    {
        button.removeEventListener(MouseEvent.ROLL_OVER, mouseOverHandler);
        button.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
        button.removeEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);
    }

    private function mouseOverHandler(e:MouseEvent):void
    {
        button.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
        button.addEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);

        onPhaseChange(WxButtonPhase.DOWN);
    }

    private function mouseOutHandler(e:MouseEvent):void
    {
        button.removeEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);

        onPhaseChange(WxButtonPhase.UP);
    }

    private function mouseDownHandler(e:MouseEvent):void
    {
        button.dispatchEvent(new WxButtonEvent(WxButtonEvent.BUTTON_PRESS));
    }
}

class ButtonBehaviorPc extends ButtonBehaviorBase
{
    private var _stage:Stage;

    private var _pressed:Boolean;

    private var _lastPressTime:int;

    public function ButtonBehaviorPc(button:WxButtonBase, onPhaseChange:Function)
    {
        super(button, onPhaseChange);
    }

    override public function get type():String
    {
        return WxButtonBehavior.PC;
    }

    override public function initEventListener():void
    {
        button.addEventListener(MouseEvent.ROLL_OVER, mouseOverHandler);

        if (button.mouseHitTest())
        {
            mouseOverHandler(null);
        }
    }

    override public function disposeEventListener():void
    {
        button.removeEventListener(MouseEvent.ROLL_OVER, mouseOverHandler);
        button.removeEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);
        button.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
        if (_stage != null)
        {
            _stage.removeEventListener(MouseEvent.MOUSE_UP, stageMouseUpHandler);
        }

        _pressed = false;
        _lastPressTime = 0;
        _stage = null;
    }

    private function mouseOverHandler(e:MouseEvent):void
    {
        button.addEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);
        button.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);

        var phase:String = _pressed ? WxButtonPhase.DOWN :
            (e == null || e.buttonDown) ? null : WxButtonPhase.OVER;
        if (phase != null)
        {
            onPhaseChange(phase);
        }
    }

    private function mouseOutHandler(e:MouseEvent):void
    {
        button.removeEventListener(MouseEvent.ROLL_OUT, mouseOutHandler);
        button.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);

        var phase:String = _pressed ? WxButtonPhase.OVER : WxButtonPhase.UP;
        onPhaseChange(phase);
    }

    private function mouseDownHandler(e:MouseEvent):void
    {
        _pressed = true;
        _stage = button.stage;
        _stage.addEventListener(MouseEvent.MOUSE_UP, stageMouseUpHandler);

        var pressTime:int = getTimer();
        var doubleClicked:Boolean = _lastPressTime > 0 && pressTime - _lastPressTime < 500;
        _lastPressTime = doubleClicked ? 0 : pressTime;

        onPhaseChange(WxButtonPhase.DOWN);

        button.dispatchEvent(new WxButtonEvent(WxButtonEvent.BUTTON_PRESS));

        if (doubleClicked)
        {
            button.dispatchEvent(new WxButtonEvent(WxButtonEvent.BUTTON_DOUBLE_CLICK));
        }
    }

    private function stageMouseUpHandler(e:MouseEvent):void
    {
        _pressed = false;
        _stage = null;
        (e.currentTarget as EventDispatcher).removeEventListener(e.type, stageMouseUpHandler);

        var clicked:Boolean = button.mouseHitTest();

        onPhaseChange(clicked ? WxButtonPhase.OVER : WxButtonPhase.UP);

        if (clicked)
        {
            button.dispatchEvent(new WxButtonEvent(WxButtonEvent.BUTTON_CLICK));
        }

        button.dispatchEvent(new WxButtonEvent(WxButtonEvent.BUTTON_RELEASE));
    }
}

